#   parsedate.py
#   From "rfc822.py
#
#   Shed Skin test
#
# Parse a date field
#
import time

_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul',
               'aug', 'sep', 'oct', 'nov', 'dec',
               'january', 'february', 'march', 'april', 'may', 'june', 'july',
               'august', 'september', 'october', 'november', 'december']
_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']

# The timezone table does not include the military time zones defined
# in RFC822, other than Z.  According to RFC1123, the description in
# RFC822 gets the signs wrong, so we can't rely on any such time
# zones.  RFC1123 recommends that numeric timezone indicators be used
# instead of timezone names.

_timezones = {'UT':0, 'UTC':0, 'GMT':0, 'Z':0,
              'AST': -400, 'ADT': -300,  # Atlantic (used in Canada)
              'EST': -500, 'EDT': -400,  # Eastern
              'CST': -600, 'CDT': -500,  # Central
              'MST': -700, 'MDT': -600,  # Mountain
              'PST': -800, 'PDT': -700   # Pacific
              }


def parsedate_tz(datain):
    """Convert a date string to a time tuple of length 11.

    Accounts for military timezones.
    """
    ####if not datain:
    ####    return None
    ####data = str(datain)
    data = str(datain).split()
    if data[0][-1] in (',', '.') or data[0].lower() in _daynames:
        # There's a dayname here. Skip it
        del data[0]
    else:
        # no space after the "weekday,"?
        i = data[0].rfind(',')
        if i >= 0:
            data[0] = data[0][i+1:]
    if len(data) == 3: # RFC 850 date, deprecated
        stuff = data[0].split('-')
        if len(stuff) == 3:
            data = stuff + data[1:]
    if len(data) == 4:
        s = data[3]
        i = s.find('+')
        if i > 0:
            data[3:] = [s[:i], s[i+1:]]
        else:
            data.append('') # Dummy tz
    if len(data) < 5:
        return None
    data = data[:5]
    [ddstr, mmstr, yystr, tmstr, tzstr] = data
    mmstr = mmstr.lower()
    if not mmstr in _monthnames:
        ddstr, mmstr = mmstr, ddstr.lower()
        if not mmstr in _monthnames:
            return None
    mm = _monthnames.index(mmstr)+1
    if mm > 12: mm = mm - 12
    if ddstr[-1] == ',':
        ddstr = ddstr[:-1]
    i = yystr.find(':')
    if i > 0:
        yystr, tmstr = tmstr, yystr
    if yystr[-1] == ',':
        yystr = yystr[:-1]
    if not yystr[0].isdigit():
        yystr, tzstr = tzstr, yystr
    if tmstr[-1] == ',':
        tmstr = tmstr[:-1]
    tmlst = tmstr.split(':')
    if len(tmlst) == 2:
        [thhstr, tmmstr] = tmlst
        tssstr = '0'
    elif len(tmlst) == 3:
        [thhstr, tmmstr, tssstr] = tmlst
    else:
        return None
    try:
        yy = int(yystr)
        dd = int(ddstr)
        thh = int(thhstr)
        tmm = int(tmmstr)
        tss = int(tssstr)
    except ValueError:
        return None
    #   Note that a returned time zone offset of None will now return as 0.
    #   ***BUG*** RFC822 caller tests for None.  Need to change interface.
    #   Added additional "hastz" value to returned tuple.
    tzoffset = 0    # Can't mix None with int for Shed Skin.
    hastz = 0
    tzstr = tzstr.upper()
    if tzstr in _timezones:
        tzoffset = _timezones[tzstr]
        hastz = 1
    else:
        try:
            tzoffset = int(tzstr)
            hastz = 1
        except ValueError:
            pass
    # Convert a timezone offset into seconds ; -0500 -> -18000
    if hastz:
        if tzoffset < 0:
            tzsign = -1
            tzoffset = -tzoffset
        else:
            tzsign = 1
        tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60)
    return (yy, mm, dd, thh, tmm, tss, 0, 1, 0, tzoffset, hastz)


def parsedate(data):
    """Convert a time string to a time tuple."""
    t = parsedate_tz(data)
    if t is None:
        return t
    return t[:9]


def mktime_tz(data):
    """Turn a 11-tuple as returned by parsedate_tz() into a UTC timestamp."""
    if not data[10] :
        # No zone info, so localtime is better assumption than GMT
        return time.mktime(data[:8] + (-1,))
    else:
        t = time.mktime(data[:8] + (0,))
        return t - data[9] - time.timezone

def formatdate(timeval):
    """Returns time format preferred for Internet standards.

    Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123

    According to RFC 1123, day and month names must always be in
    English.  If not for that, this code could use strftime().  It
    can't because strftime() honors the locale and could generated
    non-English names.
    """
    timeval = time.gmtime(timeval)
    return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
            ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")[timeval[6]],
            timeval[2],
            ("Jan", "Feb", "Mar", "Apr", "May", "Jun",
             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")[timeval[1]-1],
                                timeval[0], timeval[3], timeval[4], timeval[5])

#
# When used as script, run a small test program.
#

if __name__ == '__main__':
    TESTDATES = [
        "Tue, 9 Nov 2010 11:24:09 +0200",
        "Mon, 9 Nov 2037 18:18:07 +0800"
        ]
    for data in TESTDATES :
        s = str(data)
        vals = parsedate_tz(s)
        if vals is None :
            result = "None"
        else :    
            result = repr(vals)
        print("%s -> %s" % (s, result))
    print("Test complete.")